#include "EpilForNumberStatementInterpreter.h"
#include "EpilInterpreterException.h"
#include "EpilInterpreter.h"
#include "EpilExpressionInterpreter.h"
#include "EpilChunkInterpreter.h"
#include "EpilObjectHelper.h"


EpilForNumberStatementInterpreter::EpilForNumberStatementInterpreter( EpilInterpreter * interpreter )
: EpilInterpreterBase( interpreter )
, m_exitReason( ECER_Normal )
, m_exitLocation( EpilLexLocation::InvalidLocation )
{
}


void EpilForNumberStatementInterpreter::Run( const EpilForNumberStatementCode * code )
{
	const EpilExpressionCodeBase * beginCode = code->GetBegin();
	const EpilExpressionCodeBase * endCode = code->GetEnd();
	const EpilExpressionCodeBase * stepCode = code->GetStep();

	// begin
	EpilExpressionInterpreter eiBegin( this->GetInterpreter() );
	eiBegin.Run( beginCode );
	EpilObject * beginObj = eiBegin.GetObject();
	if( beginObj->GetType() != EOT_Number )
	{
		beginObj->ReleaseCopy();
		throw EpilInterpreterException( t("Initial value must be a number in a \'for\' loop"), beginCode->GetLocation() );
	}
	EpilNumberObject * begin = static_cast<EpilNumberObject*>( beginObj );
	double curNumber = begin->GetValue();
	begin->ReleaseCopy();

	// end
	EpilExpressionInterpreter eiEnd( this->GetInterpreter() );
	eiEnd.Run( endCode );
	EpilObject * endObj = eiEnd.GetObject();
	if( endObj->GetType() != EOT_Number )
	{
		endObj->ReleaseCopy();
		throw EpilInterpreterException( t("Limit value must be a number in a \'for\' loop"), endCode->GetLocation() );
	}
	EpilNumberObject * end = static_cast<EpilNumberObject*>( endObj );
	double endNumber = end->GetValue();
	end->ReleaseCopy();

	// step
	double stepNumber = 0.0;
	if( stepCode == NULL )
	{
		stepNumber = 1.0;
	}
	else
	{
		EpilExpressionInterpreter eiStep( this->GetInterpreter() );
		eiStep.Run( stepCode );
		EpilObject * stepObj = eiStep.GetObject();
		if( stepObj->GetType() != EOT_Number )
		{
			stepObj->ReleaseCopy();
			throw EpilInterpreterException( t("Step value must be a number in a \'for\' loop"), stepCode->GetLocation() );
		}
		EpilNumberObject * step = static_cast<EpilNumberObject*>( stepObj );
		stepNumber = step->GetValue();
		step->ReleaseCopy();
	}

	for(;;)
	{
		bool condition = stepNumber > 0 && EpilObjectHelper::IsDoubleLessThanOrEquals( curNumber, endNumber );
		condition = condition || EpilObjectHelper::IsDoubleLessThanOrEquals( stepNumber, 0 ) && EpilObjectHelper::IsDoubleGreaterThanOrEquals( curNumber, endNumber );
		if( condition == false )
		{
			break;
		}

		this->GetInterpreter()->EnterScope();
		EpilObject * variable = this->GetInterpreter()->CreateNumberObject( curNumber );
		this->GetInterpreter()->GetCurrentScope()->SetLocal( code->GetVariable(), variable );

		EpilChunkInterpreter ci( this->GetInterpreter() );
		try
		{
			ci.Run( code->GetBlock() );
		}
		catch( ... )
		{
			this->GetInterpreter()->LeaveScope();
			throw;
		}

		EpilChunkExitReason reason = ci.GetExitReason();
		if( reason == ECER_Break )
		{
			this->GetInterpreter()->LeaveScope();
			break;
		}
		if( reason == ECER_Continue )
		{
			this->GetInterpreter()->LeaveScope();
			curNumber += stepNumber;
			continue;
		}
		if( reason == ECER_Return )
		{
			this->GetInterpreter()->LeaveScope();
			m_exitReason = ECER_Return;
			m_exitLocation = ci.GetExitLocation();
			break;
		}

		this->GetInterpreter()->LeaveScope();
		curNumber += stepNumber;
	}
}